package cease.json;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

import net.sf.json.JSONException;
import net.sf.json.JsonConfig;
import net.sf.json.util.PropertySetStrategy;

/**
 * @author dhf
 */
public class CustomPropertySetStrategy extends PropertySetStrategy {
    private CustomPropertySetStrategy() {}

    private static PropertySetStrategy pss = new CustomPropertySetStrategy();

    public static PropertySetStrategy getInstance() {
        return pss;
    }

    @Override
    public void setProperty(Object bean, String key, Object value)
            throws JSONException {
        setProperty(bean, key, value, JSONUtil.getJsonConfig());
    }

    @SuppressWarnings("unchecked")
    public void setProperty(Object bean, String key, Object value,
            JsonConfig jsonConfig) throws JSONException {
        if (bean instanceof Map) {
            ((Map) bean).put(key, value);
        } else {
            Field field = null;
            try {
                field = bean.getClass().getDeclaredField(key);
            } catch (SecurityException e) {} catch (NoSuchFieldException e) {}
            if (null == field) {
                // find setter
                StringBuilder sb = new StringBuilder("set".length()
                        + key.length());
                sb.append("set").append(key.substring(0, 1).toUpperCase())
                        .append(key.substring(1));
                Method method = null;
                try {
                    if (null != value) {
                        method = bean.getClass().getDeclaredMethod(
                                sb.toString(), value.getClass());
                    } else {
                        // XXX value is null, hard to detect is ClazzType
                        Method[] methods = bean.getClass().getDeclaredMethods();
                        if (null != methods && methods.length > 0) {
                            for (Method m: methods) {
                                if (m.getName().equals(sb.toString())
                                        && m.getParameterTypes().length == 1
                                        && m.getReturnType() == void.class) {
                                    method = m;
                                    break;
                                }
                            }
                        }
                    }
                } catch (SecurityException e) {} catch (NoSuchMethodException e) {}
                if (null == method) {
                    return;
                }
                System.out.println(method);
                int methodModifiers = method.getModifiers();
                if (!Modifier.isPublic(methodModifiers)) {
                    return;
                }
                try {
                    method.invoke(bean, value);
                } catch (Exception e) {
                    throw new JSONException(e);
                }
                return;
            }

            int modifiersFlag = field.getModifiers();
            if (Modifier.isFinal(modifiersFlag)) {
                return;
            }
            if (Modifier.isPublic(modifiersFlag)
                    && jsonConfig.isIgnorePublicFields()) {
                return;
            }
            if (Modifier.isTransient(modifiersFlag)
                    && jsonConfig.isIgnoreTransientFields()) {
                return;
            }
            field.setAccessible(true);
            try {
                field.set(bean, value);
            } catch (Exception e) {
                throw new JSONException(e);
            }
        }
    }
}
